В предыдущем шаге мы создали таблицу T_REGISTRY. Последним полем в ней было FldIntIdentity, которое, если Вы заметили, я не упомянул ни одним словом. И вот почему.
О важности уникального кода записи говорить не будем. Первичный ключ таблицы - одно из основополагающих положений реляционных баз данных. Я даже придумал следующий диалог:
Вопрос: - Может ли в таблице быть только одно поле ? Ответ: - Может - это поле уникального кода записи (первичный ключ) !
К сожалению, у VFP нет встроенного механизма присвоения значения первичного ключа для новых записей в таблице. Есть физический номер записи, который можно получить функцией recno(). Использовать его для связей между таблицами КАТЕГОРИЧЕСКИ НЕ РЕКОМЕНДУЮ !!! VFP не удаляет записи физически, а "метит" их как удаленные. Обработка удаленных записей зависит от команды SET DELETED ON | OFF. После команды PACK(физическое удаление удаленных записей и поджатие таблицы), а выполнять ее приходится, физические номера записей смещаются. Не выполнять команду PACK никогда ? ("Никогда не говори никогда"). Заниматься поиском удаленных записей и их восстановлением - было, пробовали, ничего хорошего:
Рассуждать на эту тему можно бесконечно, но что делать ? ("Хватит рассуждать - трясти надо"). Создадим свой счетчик. Вариантов может быть несколько.
Использование sys(2015) зависит от системного таймера Вашего компьютера. Не приемлемо для локальной машины, а про сеть и говорить нечего.
Можно создать системную таблицу со списком всех таблиц и хранить для каждой таблицы максимальное значение. В этом случае необходимо предусмотреть блокировку записи функцией RLOCK() для одновременной работы нескольких пользователей. В общем работает.
Но, хотелось бы, обойтись без блокировок. Их еще будет много. Добавим в базу данных D_SYSTEM папка System таблицу T_STATIONS следующей структуры:
| Наименование | Тип | Ширина | Десятичные знаки |
|---|---|---|---|
| FldStrStationName | Char | 20 | Станция |
| FldIntStationCode | Int | Код станции | |
| FldIntLastKey | Int | Счетчик | |
| FldIntIdentity | Int | Уникальный код |
Заполним нашу таблицу примером:
| FldStrStationName | FldIntStationCode | FldIntLastKey | FldIntIdentity |
|---|---|---|---|
| LOCAL_WORKSTATION | 100 000 000 000 000 | 100 000 000 565 321 | 1 |
| NT_SERVER_P100 | 101 000 000 000 000 | 101 000 002 324 147 | 2 |
| NT_WKS_C300 | 102 000 000 000 000 | 102 000 000 022 896 | 3 |
| STATION_1 | 103 000 000 000 000 | 103 000 000 001 589 | 4 |
О структуре можно спорить, но, главное, понять принцип организации счетчика. При старте нашего приложения функцией sys(0) мы получаем сетевое имя машины. Функция sys(0) возвращает сетевое имя машины и имя пользователя, разделенные символом #:
?sys(0) MachineName # UserName
Для поля FldStrStationName необходимо создать индекс TAG_NAME типа Primary. А теперь пишем функцию для получения нового значения счетчика GetNewCodeUniq():
*
* function GetNewCodeUniq
local intLocReturn, strLocSysZero, strLocMachine
intLocReturn = 0
strLocSysZero = sys(0)
strLocMachine = padr(alltrim(left(strLocSysZero,at("#",strLocSysZero)-1)),20)
* для отдельно стоящей машины
if empty(strLocMachine)
strLocMachine = padr("LOCAL_WORKSTATION",20)
endif
if (OpenTable(".\System\T_Stations.DBF", "AliasSystemStations") > 0)
if seek(strLocMachine,"AliasSystemStation","TAG_NAME")
intLocReturn = AliasSystemStation.FldIntLastKey + 1
replace FldIntLastKey with intLocReturn in AliasSystemStation
endif
endif
return intLocReturn
*
Обработку возникающих ошибок каждый должен придумать самостоятельно. Итак, за счет префикса станции, мы обеспечили уникальность счетчика для каждой машины в сети без применения блокировок записей.
Пример использования:
*
local intLocNewCodeUniq
intLocNewCodeUniq = GetNewCodeUniq()
*
insert into MyTable (FldStrName,FldIntIdentity) values ("Новое наименование",intLocNewCodeUniq)
*
Вот теперь мы можем, и должны создавать во всех наших таблицах для поля FldIntIdentity индекс TAG_CODE типа Primary. Если в таблице необходимо обеспечить несколько уникальных индексов, то только один из них может быть Primary. В этом случае другие такие индексы нужно создавать типа Candidate. Разница только в названии типа. Обычные индексы, которые могут содержать повторяющиеся значения, создаются типа Regular. Тип индекса Unique включен для обратной совместимости с предыдущими версиями FoxPro.